package panda.query.scan;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import panda.query.aggr.AggregateFn;
import panda.query.struct.Attribute;
import panda.query.struct.Constant;
import panda.query.struct.NullConstant;

/**
 * 
 * @author Tian Yuan
 */
public class GroupByScanner implements Scanner {
	private Scanner s;
	private List<Attribute> attrs;
	private List<Constant> currentVal;
	private Map<Attribute, Integer> aggmap;
	private List<AggregateFn> aggrFn;
	private boolean hasNext;
//	private Comparator cmp;
	
	/**
	 * 
	 * @param s
	 * @param attrs
	 * @param aggrFn
	 */
	public GroupByScanner(Scanner s, List<Attribute> attrs, List<AggregateFn> aggrFn) {
		this.s = s;
		this.attrs = attrs;
		this.aggrFn = aggrFn;
		this.aggmap = new HashMap<Attribute, Integer>();
		currentVal = new ArrayList<Constant>(attrs.size());
		for (int i = 0; i < attrs.size(); i++) {
			aggmap.put(attrs.get(i), i);
			currentVal.add(null);
		}
		init();
	}
	
	private void saveCurrentValue() {
		for (int i = 0; i < attrs.size(); i++)
			currentVal.set(i, s.getValue(attrs.get(i)));
	}
	
	private boolean isEqual() {
		if (attrs.size() == 0) return true;
		
		for (int i = 0; i < attrs.size(); i++) {
			Constant a = currentVal.get(i);
			Constant b = s.getValue(attrs.get(i));
			if (!(a instanceof NullConstant && b instanceof NullConstant)
					&& a.compareTo(b) != 0)
				return false;
		}
		return true;
	}
	
	public void init() {
		s.init();
		hasNext = s.next();
	}

	public void close() {
		s.close();
	}

	public boolean next() {
		if (!hasNext) return false;
		saveCurrentValue();
		for (int i = 0; i < aggrFn.size(); i++)
			aggrFn.get(i).first(s);
		
		while ((hasNext = s.next()) && isEqual()) {
			for (int i = 0; i < aggrFn.size(); i++)
				aggrFn.get(i).next(s);
		} 
		return true;
	}

	public Constant getValue(Attribute attr) {
		if (attrs.contains(attr))
			return currentVal.get(aggmap.get(attr));
		for (int i = 0; i < aggrFn.size(); i++)
			if (aggrFn.get(i).getAttribute() == attr)
				return aggrFn.get(i).getValue();
		return null;
	}

	public boolean hasAttribute(Attribute name) {
		if (attrs.contains(name))
			return true;
		for (int i = 0; i < aggrFn.size(); i++)
			if (aggrFn.get(i).getAttribute() == name)
				return true;
		return false;
	}


}
